
using System;
using System.Diagnostics;
using Microsoft.Win32;
using System.Linq;

namespace RegHelper
{
    /// <summary>
    /// read+write registry parameter. (the key-value part)
    /// </summary>
    public class Param : ParamSpec, IDisposable
    {
        RegPath _container = null;
        string _keyName = null; // not written yet to registry
        RegistryValueKind _kind = RegistryValueKind.Unknown;


        /// <summary>
        /// features: 
        /// setting, and getting the parameter value.
        /// access to the containing folder (RegPath object)
        /// </summary>
        /// <param name="root">enum RootRegistryPath: choose one base "folder" to work from</param>
        ///<param name="fullpath">path after the base folder, including the parameter name. use forward slashes like so: "Folder1\\Folder2\\myFolder\\myKey"</param>
        /// <param name="setThisKind">the type of data you want to insert. if the key exists, this argument is ignored.</param>
        public Param(RootRegistryPath root, string fullpath, RegistryValueKind setThisKind)
        {
            var folder = containingFolder(fullpath);
            var key = keyName(fullpath);

            var path = RegPath.NewPath(root, folder, false);
            this._container = path;
            this._keyName = key;
            if (this._container.ls().Contains(key))
                this._kind = path.Handler.GetValueKind(key);
            else
                this._kind = setThisKind;

        }

        private string keyName(string path)
        {
            Debug.Assert(path.Contains("\\"));
            return path.Substring(path.LastIndexOf('\\')).Trim('\\');
        }

        private string containingFolder(string path)
        {
            Debug.Assert(path.Contains("\\"));
            var parent = path.Substring(0, path.LastIndexOf('\\')); //.Trim('\\');
            return parent;
        }

        #region ParamSpec Members

        /// <summary>
        /// Deletes the current value from the registry.
        /// </summary>
        /// <returns>true if value was actually deleted, false - anything else.
        /// (even doesn't exist returns false)
        /// </returns>
        public bool Delete()
        {
            if (this.Exist() == null)
                return false;

            else if ((bool)this.Exist())
            {
                // okay to continue.
                try
                {
                    this._container.Handler.DeleteValue(this._keyName);
                    return true;
                }
                // if necessary add more catch phrases here.
                catch (Exception e)
                {
                    Util.PrintException(e);
                    return false;
                }
            }
            else // doesn't exist, so we don't delete.
                return false;
        }

        /// <summary>
        /// checks if value exists
        /// </summary>
        /// <returns>
        /// true  -> exists
        /// false -> doesn't exist
        /// null  -> don't know
        /// </returns>
        public bool? Exist()
        {
            object obj;
            bool res = TryGetValue(out obj);
            if (res) // operation worked:
            {
                if (obj == null) // got null, meaning doesn't exist.
                    return false;
                else
                    return true;
            }
            else // operation failed, so we don't know.
                return null;
        }

        public bool Valid
        {
            get
            {
                // valid when _container not null;
                return _container != null && _container.Valid;
            }
        }

        public RegPath InFolder()
        {
            return this._container;
        }

        public RegistryValueKind Kind
        {
            get
            {
                return this._kind;
            }
        }



        public bool TryGetValue(out object result) // why? because getvalue returns null when the value doesn't exist.
        {
            result = default(object);
            if (this._container.Handler == null)
                return false;
            else
            {
                if (this._container != null && this._container.Handler != null)
                {
                    result = this._container.Handler.GetValue(this._keyName);
                    return true;
                }
                else
                    return false;
            }

        }
        /// <summary>
        /// tries to set or create a value to the current key.
        /// </summary>
        /// <param name="value">a value of the same type that was mentioned when creating the Param object</param>
        /// <returns></returns>
        public bool SetValue(object value)
        {
            try
            {
                if (this._container != null && this._container.Handler != null)
                {
                    this._container.Handler.SetValue(this._keyName, value, this._kind);
                    return true;
                }
                else
                    return false;
            }
            catch (Exception e)
            {
                Util.PrintException(e);
                return false;
            }


        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            this._container.Dispose();
        }

        #endregion



    }
}


